Išsamiai susipažinkite su pagrindiniais šiukšlių surinkimo algoritmais, kurie varo šiuolaikines vykdymo sistemas, itin svarbūs atminties valdymui ir programų našumui visame pasaulyje.
Vykdymo sistemos: giluminė šiukšlių surinkimo algoritmų analizė
Sudėtingame kompiuterijos pasaulyje vykdymo sistemos yra nematomi varikliai, kurie prikelia mūsų programinę įrangą gyvybei. Jos valdo išteklius, vykdo kodą ir užtikrina sklandų programų veikimą. Daugelio šiuolaikinių vykdymo sistemų šerdyje yra kritiškai svarbus komponentas: šiukšlių surinkimas (ŠS). ŠS yra automatinis atminties, kurios programa nebenaudoja, atlaisvinimo procesas, padedantis išvengti atminties nutekėjimo ir užtikrinti efektyvų išteklių panaudojimą.
Programuotojams visame pasaulyje ŠS supratimas yra ne tik švaresnio kodo rašymas; tai yra apie patikimų, našumo reikalavimus atitinkančių ir keičiamo dydžio programų kūrimą. Ši išsami analizė nagrinės pagrindines sąvokas ir įvairius algoritmus, kurie varo šiukšlių surinkimą, teikdama vertingų įžvalgų įvairių techninių sričių specialistams.
Atminties valdymo būtinybė
Prieš pradedant nagrinėti konkrečius algoritmus, būtina suprasti, kodėl atminties valdymas yra toks svarbus. Tradicinėse programavimo paradigmose kūrėjai rankiniu būdu skiria ir atlaisvina atmintį. Nors tai suteikia tikslų valdymą, tai taip pat yra pagarsėjęs klaidų šaltinis:
- Atminties nutekėjimai: Kai paskirta atmintis nebenaudojama, bet nėra aiškiai atlaisvinama, ji lieka užimta, todėl palaipsniui išeikvojama turima atmintis. Ilgainiui tai gali sulėtinti programos veikimą arba sukelti visiška avarija.
- Kabantys rodyklės: Jei atmintis atlaisvinama, bet rodyklė vis dar į ją nurodo, bandymai pasiekti tą atmintį sukelia nenustatytą elgesį, dažnai lemia saugumo pažeidimus arba avarijas.
- Dvigubo atlaisvinimo klaidos: Atminties, kuri jau buvo atlaisvinta, atlaisvinimas taip pat sukelia gedimus ir nestabilumą.
Automatinis atminties valdymas, naudojant šiukšlių surinkimą, siekia palengvinti šią naštą. Vykdymo sistema prisiima atsakomybę už nenaudojamos atminties identifikavimą ir atlaisvinimą, leidžianti kūrėjams sutelkti dėmesį į programos logiką, o ne į žemo lygio atminties manipuliavimą. Tai ypač svarbu pasauliniame kontekste, kur įvairios techninės įrangos galimybės ir diegimo aplinkos reikalauja atsparios ir efektyvios programinės įrangos.
Pagrindinės šiukšlių surinkimo sąvokos
Keletas pagrindinių sąvokų yra visų šiukšlių surinkimo algoritmų pagrindas:
1. Pasiekiamumas
Daugelio ŠS algoritmų pagrindinis principas yra pasiekiamumas. Objektas laikomas pasiekiamu, jei yra kelias iš žinomų, „gyvų“ šaknų iki to objekto. Šaknys paprastai apima:
- Globalūs kintamieji
- Lokalūs kintamieji vykdymo dėkle
- CPU registrai
- Statiniai kintamieji
Bet koks objektas, kuris nėra pasiekiamas iš šių šaknų, laikomas šiukšle ir gali būti atlaisvintas.
2. Šiukšlių surinkimo ciklas
Tipiškas ŠS ciklas apima kelias fazes:
- Žymėjimas: ŠS pradeda nuo šaknų ir pereina per objektų grafiką, pažymėdama visus pasiekiamus objektus.
- Valymas (arba suspaudimas): Po žymėjimo ŠS iteruoja per atmintį. Nepažymėti objektai (šiukšlės) yra atlaisvinami. Kai kuriuose algoritmuose pasiekiami objektai taip pat perkeliami į gretimas atminties vietas (suspaudimas), siekiant sumažinti fragmentaciją.
3. Pauzės
Didelis iššūkis ŠS yra potencialios „sustabdyti pasaulį“ (STW) pauzės. Per šias pauzes programos vykdymas sustabdomas, kad ŠS galėtų atlikti savo operacijas be trikdžių. Ilgos STW pauzės gali žymiai paveikti programos reakciją, o tai yra kritiškai svarbu vartotojui skirtoms programoms bet kurioje pasaulinėje rinkoje.
Pagrindiniai šiukšlių surinkimo algoritmai
Per daugelį metų buvo sukurti įvairūs ŠS algoritmai, kiekvienas turintis savo stiprybių ir silpnybių. Išnagrinėsime kai kuriuos dažniausiai naudojamus:
1. Žymėjimas ir valymas (Mark-and-Sweep)
Žymėjimo ir valymo algoritmas (Mark-and-Sweep) yra viena seniausių ir pagrindinių ŠS technikų. Jis veikia dviem atskiromis fazėmis:
- Žymėjimo fazė: ŠS pradeda nuo šaknų rinkinio ir pereina per visą objektų grafiką. Kiekvienas aptiktas objektas yra pažymimas.
- Valymo fazė: ŠS tada nuskenuoja visą krūvą. Bet koks objektas, kuris nebuvo pažymėtas, laikomas šiukšle ir yra atlaisvinamas. Atlaisvinta atmintis pridedama prie laisvų sąrašo būsimiems atminties paskyrimams.
Privalumai:
- Koncepciškai paprasta ir plačiai suprantama.
- Efektyviai tvarko ciklines duomenų struktūras.
Trūkumai:
- Našumas: Gali būti lėtas, nes reikia pereiti per visą krūvą ir nuskenuoti visą atmintį.
- Fragmentacija: Atmintis fragmentuojasi, nes objektai skiriami ir atlaisvinami skirtingose vietose, o tai gali sukelti paskyrimo klaidas, net jei yra pakankamai bendros laisvos atminties.
- STW pauzės: Paprastai susijęs su ilgomis „sustabdyti pasaulį“ pauzėmis, ypač didelėse krūvose.
Pavyzdys: Ankstyvosios Java šiukšlių surinkimo versijos naudojo pagrindinį žymėjimo ir valymo metodą.
2. Žymėjimas ir suspaudimas (Mark-and-Compact)
Siekdamas išspręsti „Mark-and-Sweep“ fragmentacijos problemą, „Mark-and-Compact“ algoritmas prideda trečią fazę:
- Žymėjimo fazė: Identinė „Mark-and-Sweep“ fazei, ji pažymi visus pasiekiamus objektus.
- Suspaudimo fazė: Po žymėjimo ŠS perkelia visus pažymėtus (pasiekiamus) objektus į gretimus atminties blokus. Tai pašalina fragmentaciją.
- Valymo fazė: ŠS tada praeina per atmintį. Kadangi objektai buvo suspausti, laisva atmintis dabar yra vienas vientisas blokas krūvos gale, todėl būsimi paskirstymai atliekami labai greitai.
Privalumai:
- Pašalina atminties fragmentaciją.
- Greitesni vėlesni paskirstymai.
- Vis dar tvarko ciklines duomenų struktūras.
Trūkumai:
- Našumas: Suspaudimo fazė gali būti skaičiavimo atžvilgiu brangi, nes ji apima daugelio objektų perkėlimą atmintyje.
- STW pauzės: Vis dar sukelia reikšmingas STW pauzes dėl būtinybės perkelti objektus.
Pavyzdys: Šis metodas yra daugelio pažangesnių surinkėjų pagrindas.
3. Kopijavimo šiukšlių surinkimas (Copying Garbage Collection)
Kopijavimo ŠS padalija krūvą į dvi erdves: „iš-erdvė“ (From-space) ir „į-erdvė“ (To-space). Paprastai nauji objektai paskiriami „iš-erdvėje“.
- Kopijavimo fazė: Kai inicijuojamas ŠS, ŠS pereina per „iš-erdvę“, pradedant nuo šaknų. Pasiekiami objektai kopijuojami iš „iš-erdvės“ į „į-erdvę“.
- Erdvių keitimas: Kai visi pasiekiami objektai yra nukopijuoti, „iš-erdvė“ lieka tik šiukšlėmis, o „į-erdvėje“ yra visi gyvi objektai. Tada erdvių vaidmenys sukeičiami. Senoji „iš-erdvė“ tampa nauja „į-erdve“, paruošta kitam ciklui.
Privalumai:
- Be fragmentacijos: Objektai visada kopijuojami vientisai, todėl „į-erdvėje“ nėra fragmentacijos.
- Greitas paskyrimas: Paskirstymai yra greiti, nes tereikia perstumti rodyklę dabartinėje paskyrimo erdvėje.
Trūkumai:
- Erdvės sąnaudos: Reikalauja dvigubai daugiau atminties nei viena krūva, nes aktyvios dvi erdvės.
- Našumas: Gali būti brangus, jei daug objektų yra gyvi, nes visi gyvi objektai turi būti nukopijuoti.
- STW pauzės: Vis dar reikalauja STW pauzių.
Pavyzdys: Dažnai naudojamas „jaunosios“ kartos rinkimui kartų šiukšlių surinkėjuose.
4. Kartų šiukšlių surinkimas (Generational Garbage Collection)
Šis metodas pagrįstas kartų hipoteze, kuri teigia, kad dauguma objektų turi labai trumpą gyvavimo ciklą. Kartų ŠS padalija krūvą į kelias kartas:
- Jaunoji karta: Kur skiriami nauji objektai. ŠS rinkimai čia yra dažni ir greiti (mažieji ŠS).
- Senoji karta: Objektai, kurie išgyvena kelis mažuosius ŠS, yra perkeliami į senąją kartą. ŠS rinkimai čia yra retesni ir kruopštesni (didieji ŠS).
Kaip tai veikia:
- Nauji objektai paskiriami jaunojoje kartoje.
- Mažieji ŠS (dažnai naudojant kopijavimo surinkėją) dažnai atliekami jaunojoje kartoje. Išgyvenę objektai perkeliami į senąją kartą.
- Didieji ŠS atliekami rečiau senojoje kartoje, dažnai naudojant „Mark-and-Sweep“ arba „Mark-and-Compact“.
Privalumai:
- Pagerintas našumas: Žymiai sumažina visos krūvos rinkimo dažnumą. Dauguma šiukšlių randama jaunojoje kartoje, kuri surenkama greitai.
- Sumažintas pauzių laikas: Mažieji ŠS yra daug trumpesni nei viso krūvos ŠS.
Trūkumai:
- Sudėtingumas: Sudėtingesnis įgyvendinimas.
- Perkėlimo sąnaudos: Objektai, išgyvenę mažuosius ŠS, patiria perkėlimo išlaidas.
- Prisiminti rinkiniai: Norint tvarkyti objektų nuorodas iš senosios kartos į jaunąją kartą, reikalingi „prisiminti rinkiniai“, kurie gali padidinti sąnaudas.
Pavyzdys: Java virtualioji mašina (JVM) plačiai naudoja kartų ŠS (pvz., su surinkėjais, tokiais kaip „Throughput Collector“, CMS, G1, ZGC).
5. Nuorodų skaičiavimas (Reference Counting)
Vietoj pasiekiamumo sekimo, nuorodų skaičiavimas kiekvienam objektui priskiria skaičių, nurodantį, kiek nuorodų rodo į jį. Objektas laikomas šiukšle, kai jo nuorodų skaičius sumažėja iki nulio.
- Didinimas: Kai sukuriama nauja nuoroda į objektą, jo nuorodų skaičius padidinamas.
- Mažinimas: Kai nuoroda į objektą pašalinama, jo skaičius sumažinamas. Jei skaičius tampa nulis, objektas nedelsiant atlaisvinamas.
Privalumai:
- Be pauzių: Atlaisvinimas vyksta laipsniškai, kai nuorodos numetamos, išvengiant ilgų STW pauzių.
- Paprastumas: Koncepciškai paprasta.
Trūkumai:
- Ciklinės nuorodos: Pagrindinis trūkumas yra nesugebėjimas surinkti ciklines duomenų struktūras. Jei objektas A rodo į B, o B rodo atgal į A, net jei nėra išorinių nuorodų, jų nuorodų skaičius niekada nepasieks nulio, o tai sukels atminties nutekėjimą.
- Papildomos sąnaudos: Skaičių didinimas ir mažinimas prideda sąnaudų kiekvienai nuorodos operacijai.
- Nenuspėjamas elgesys: Nuorodų mažinimo tvarka gali būti nenuspėjama, turinti įtakos atminties atlaisvinimo laikui.
Pavyzdys: Naudojamas Swift (ARC – automatinis nuorodų skaičiavimas), Python ir Objective-C.
6. Inkrementinis šiukšlių surinkimas (Incremental Garbage Collection)
Siekiant dar labiau sumažinti STW pauzių laiką, inkrementiniai ŠS algoritmai atlieka ŠS darbą mažais fragmentais, persipynusios ŠS operacijos su programos vykdymu. Tai padeda išlaikyti trumpą pauzių laiką.
- Fazinės operacijos: Žymėjimo ir valymo/suspaudimo fazės suskaidomos į mažesnius žingsnius.
- Persipynimas: Programos gija gali vykdytis tarp ŠS darbo ciklų.
Privalumai:
- Trumpesnės pauzės: Žymiai sumažina STW pauzių trukmę.
- Pagerintas atsakas: Geriau tinka interaktyvioms programoms.
Trūkumai:
- Sudėtingumas: Sudėtingesnis įgyvendinimas nei tradicinių algoritmų.
- Našumo sąnaudos: Gali sukelti tam tikrų papildomų sąnaudų dėl būtinybės koordinuoti ŠS ir programos gijas.
Pavyzdys: „Concurrent Mark Sweep“ (CMS) surinkėjas senesnėse JVM versijose buvo ankstyvas inkrementinio surinkimo bandymas.
7. Lygiagretus šiukšlių surinkimas (Concurrent Garbage Collection)
Lygiagretūs ŠS algoritmai didžiąją dalį savo darbo atlieka lygiagrečiai su programos gijomis. Tai reiškia, kad programa toliau veikia, o ŠS identifikuoja ir atlaisvina atmintį.
- Koordinuotas darbas: ŠS gijos ir programos gijos veikia lygiagrečiai.
- Koordinavimo mechanizmai: Reikalingi sudėtingi mechanizmai, užtikrinantys nuoseklumą, tokie kaip trijų spalvų žymėjimo algoritmai ir rašymo barjerai (kurie seka programos atliktus objektų nuorodų pakeitimus).
Privalumai:
- Minimalios STW pauzės: Siekia labai trumpo ar net „be pauzių“ veikimo.
- Didelis pralaidumas ir atsakas: Puikiai tinka programoms, turinčioms griežtus vėlavimo reikalavimus.
Trūkumai:
- Sudėtingumas: Ypač sudėtinga teisingai suprojektuoti ir įgyvendinti.
- Pralaidumo sumažinimas: Kartais gali sumažinti bendrą programos pralaidumą dėl lygiagrečių operacijų ir koordinavimo sąnaudų.
- Atminties sąnaudos: Gali prireikti papildomos atminties pokyčiams sekti.
Pavyzdys: Šiuolaikiniai surinkėjai, tokie kaip G1, ZGC ir Shenandoah Java kalboje, bei ŠS Go ir .NET Core aplinkose yra labai lygiagretūs.
8. G1 (Garbage-First) surinkėjas
G1 surinkėjas, pristatytas Java 7 ir tapęs numatytuoju Java 9 versijoje, yra serverio stiliaus, regionais pagrįstas, kartų ir lygiagretus surinkėjas, skirtas suderinti pralaidumą ir vėlavimą.
- Regionais pagrįstas: Padalija krūvą į daugybę mažų regionų. Regionai gali būti Eden, Survivor arba Old.
- Kartų: Išlaiko kartų savybes.
- Lygiagretus ir paralelinė: Daugumą darbo atlieka lygiagrečiai su programos gijomis ir naudoja kelias gijas evakuacijai (gyvų objektų kopijavimui).
- Orientuotas į tikslą: Leidžia vartotojui nustatyti norimą pauzės laiko tikslą. G1 bando pasiekti šį tikslą, pirmiausia surinkdamas regionus su daugiausiai šiukšlių (todėl ir „Garbage-First“).
Privalumai:
- Subalansuotas našumas: Tinka plačiam programų spektrui.
- Nuspėjamas pauzių laikas: Žymiai pagerintas pauzių laiko nuspėjamumas, palyginti su senesniais surinkėjais.
- Gerai tvarko dideles krūvas: Efektyviai keičia mastelį su didelėmis krūvų dydžiais.
Trūkumai:
- Sudėtingumas: Iš prigimties sudėtingas.
- Galimos ilgesnės pauzės: Jei tikslinis pauzės laikas yra agresyvus ir krūva yra labai fragmentuota su gyvais objektais, vienas ŠS ciklas gali viršyti tikslą.
Pavyzdys: Daugelio šiuolaikinių Java programų numatytasis ŠS.
9. ZGC ir Shenandoah
Tai yra naujesni, pažangūs šiukšlių surinkėjai, sukurti ypač trumpam pauzių laikui, dažnai siekiant submilisekundinių pauzių, net ir labai didelėse krūvose (terabaituose).
- Suspaudimas krovimo metu: Jie atlieka suspaudimą lygiagrečiai su programa.
- Labai lygiagretus: Beveik visas ŠS darbas vyksta lygiagrečiai.
- Regionais pagrįstas: Naudoja regionais pagrįstą metodą, panašų į G1.
Privalumai:
- Ypač mažas vėlavimas: Siekia labai trumpų, nuoseklių pauzių laiko.
- Keičiamumas: Puikiai tinka programoms su didžiulėmis krūvomis.
Trūkumai:
- Pralaidumo poveikis: Gali turėti šiek tiek didesnes CPU sąnaudas nei į pralaidumą orientuoti surinkėjai.
- Brandumas: Santykinai naujesni, nors sparčiai bręstantys.
Pavyzdys: ZGC ir Shenandoah yra prieinami naujausiose OpenJDK versijose ir tinka vėlavimui jautrioms programoms, tokioms kaip finansinių sandorių platformos ar didelio masto žiniatinklio paslaugos, aptarnaujančios pasaulinę auditoriją.
Šiukšlių surinkimas skirtingose vykdymo aplinkose
Nors principai yra universalūs, ŠS įgyvendinimas ir niuansai skiriasi įvairiose vykdymo aplinkose:
- Java virtualioji mašina (JVM): Istoriškai JVM buvo ŠS inovacijų priešakyje. Ji siūlo įdiegiamą ŠS architektūrą, leidžiančią kūrėjams pasirinkti iš įvairių surinkėjų (Serial, Parallel, CMS, G1, ZGC, Shenandoah), atsižvelgiant į jų programos poreikius. Šis lankstumas yra itin svarbus optimizuojant našumą įvairiuose pasauliniuose diegimo scenarijuose.
- .NET Common Language Runtime (CLR): .NET CLR taip pat pasižymi sudėtingu ŠS. Ji siūlo tiek kartų, tiek kompaktišką šiukšlių surinkimą. CLR ŠS gali veikti darbo stoties režimu (optimizuotas kliento programoms) arba serverio režimu (optimizuotas daugiaprocesoriaus serverio programoms). Ji taip pat palaiko lygiagretų ir foninį šiukšlių surinkimą, siekiant sumažinti pauzes.
- Go vykdymo aplinka: Go programavimo kalba naudoja lygiagretų, trijų spalvų žymėjimo ir valymo šiukšlių surinkėją. Jis sukurtas mažam vėlavimui ir dideliam lygiagretumui, atitinkantis Go filosofiją kurti efektyvias lygiagrečias sistemas. Go ŠS siekia išlaikyti labai trumpas pauzes, paprastai mikrosekundžių eilės.
- JavaScript varikliai (V8, SpiderMonkey): Šiuolaikiniai JavaScript varikliai naršyklėse ir Node.js naudoja kartų šiukšlių surinkėjus. Jie naudoja tokias technikas kaip žymėjimas ir valymas ir dažnai integruoja inkrementinį surinkimą, kad išlaikytų vartotojo sąsajos interakcijas atsakingas.
Tinkamo ŠS algoritmo pasirinkimas
Tinkamo ŠS algoritmo pasirinkimas yra kritiškai svarbus sprendimas, turintis įtakos programos našumui, keičiamumui ir vartotojo patirčiai. Nėra vieno universalaus sprendimo. Apsvarstykite šiuos veiksnius:
- Programos reikalavimai: Ar jūsų programa yra jautri vėlavimui (pvz., prekyba realiuoju laiku, interaktyvios žiniatinklio paslaugos) ar orientuota į pralaidumą (pvz., paketinis apdorojimas, moksliniai skaičiavimai)?
- Krūvos dydis: Labai didelėms krūvoms (dešimtys ar šimtai gigabaitų) dažnai teikiama pirmenybė surinkėjams, sukurtiems keičiamumui ir mažam vėlavimui (pvz., G1, ZGC, Shenandoah).
- Lygiagretumo poreikiai: Ar jūsų programa reikalauja didelio lygiagretumo? Lygiagretus ŠS gali būti naudingas.
- Kūrimo pastangos: Paprastesnius algoritmus gali būti lengviau suprasti, tačiau jie dažnai susiję su našumo kompromisais. Pažangūs surinkėjai siūlo geresnį našumą, bet yra sudėtingesni.
- Tikslinė aplinka: Diegimo aplinkos (pvz., debesis, įterptosios sistemos) galimybės ir apribojimai gali turėti įtakos jūsų pasirinkimui.
Praktiniai patarimai ŠS optimizavimui
Be tinkamo algoritmo pasirinkimo, galite optimizuoti ŠS našumą:
- Derinti ŠS parametrus: Dauguma vykdymo aplinkų leidžia derinti ŠS parametrus (pvz., krūvos dydį, kartų dydžius, konkrečias surinkėjo parinktis). Tam dažnai reikia profiliavimo ir eksperimentavimo.
- Objektų telkimas: Objektų pakartotinis naudojimas per telkimą gali sumažinti atminties paskyrimų ir atlaisvinimų skaičių, taip sumažinant ŠS apkrovą.
- Vengti nereikalingų objektų kūrimo: Atkreipkite dėmesį į didelio skaičiaus trumpalaikių objektų kūrimą, nes tai gali padidinti ŠS darbo krūvį.
- Protingai naudoti silpnas/minkštas nuorodas: Šios nuorodos leidžia surinkti objektus, jei trūksta atminties, o tai gali būti naudinga talpykloms.
- Profiluoti savo programą: Naudokite profiliavimo įrankius, kad suprastumėte ŠS elgesį, nustatytumėte ilgas pauzes ir nurodytumėte sritis, kuriose ŠS sąnaudos yra didelės. Tokie įrankiai kaip „VisualVM“, „JConsole“ (Java), „PerfView“ (.NET) ir „`pprof`“ (Go) yra neįkainojami.
Šiukšlių surinkimo ateitis
Tęsiamos pastangos siekti dar mažesnio vėlavimo ir didesnio efektyvumo. Būsimi ŠS tyrimai ir plėtra greičiausiai bus sutelkti į:
- Tolesnis pauzių mažinimas: Siekiama tikrai „be pauzių“ arba „beveik be pauzių“ surinkimo.
- Aparatinės įrangos pagalba: Tiriama, kaip aparatinė įranga gali padėti ŠS operacijoms.
- AI/ML valdomas ŠS: Potencialiai naudojant mašininį mokymąsi, siekiant dinamiškai pritaikyti ŠS strategijas prie programos elgesio ir sistemos apkrovos.
- Suderinamumas: Geresnis integravimas ir suderinamumas tarp skirtingų ŠS įgyvendinimų ir kalbų.
Išvada
Šiukšlių surinkimas yra šiuolaikinių vykdymo sistemų kertinis akmuo, tyliai valdantis atmintį, kad užtikrintų sklandų ir efektyvų programų veikimą. Nuo pagrindinio „Mark-and-Sweep“ iki ypač mažo vėlavimo ZGC, kiekvienas algoritmas žymi evoliucinį žingsnį atminties valdymo optimizavime. Programuotojams visame pasaulyje, tvirtas šių technikų supratimas suteikia galimybę kurti našesnę, keičiamo dydžio ir patikimesnę programinę įrangą, kuri gali sėkmingai veikti įvairiose pasaulio aplinkose. Suprasdami kompromisus ir taikydami geriausią praktiką, galime išnaudoti ŠS galią kurti naujos kartos išskirtines programas.